package com.excel.validators.core.extension;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

/**
 * @author wangxiaoliang10
 *
 * 2017年8月5日
 * 
 * jar更新完成后，该jar的classpath将会在maven环境下丢失
 * ，原因在于maven有自己的classloader机制，或者由于jvm对
 * jarEntry更新后需要重新reload该jar的原因（猜测），在此不作
 * 深入了解，通过获取java进程参数，重新编译正在作用工程，完成编译。
 * 该方案是最终的处理方案，通过classload的改造方案失败
 */
final class MakeInstall {

	/**
	 * 修改文件
	 */
	private String entryName;
	
	/**
	 * jar路径
	 */
	private String jarPath;
	
	/**
	 * jar file
	 */
	private JarFile jf;
	
	public MakeInstall() {
		try {
				init();
		} catch (Exception e) {
				e.printStackTrace();
		}
	}

	/**
	 * 初始化参数
	 * @throws FileNotFoundException 
	 * @throws IOException 
	 */
	private void init() throws FileNotFoundException, IOException {
			this.jarPath = getJarPath();
			this.entryName = getJarEntryPath();
			this.jf = createJar();
	}

	private JarFile createJar() throws IOException {
			if (jarPath.endsWith(".jar"))
					return new JarFile(jarPath);
			return null;
	}
	
	protected Map<JarEntry,byte[]> read() throws IOException {
			Map<JarEntry,byte[]> tm = new LinkedHashMap<JarEntry,byte[]>();
			Enumeration<JarEntry> entrys = jf.entries();
			while (entrys.hasMoreElements()) {
					JarEntry entry = (JarEntry) entrys.nextElement();
					byte[] b = getByte(jf.getInputStream(entry));
					tm.put(entry, b);
			}
			return tm;
	}
	
	/**
	 * 初始化jark中entry的类路径,java中jarEntry的path只能用正斜线表示
	 * @return
	 */
	private String getJarEntryPath() {
			String cls = MakeInstall.class.getName();
			String target = cls.replaceAll("\\.", "/").
					replace("MakeInstall", "ModelAnalysis.class");
			return target;
	}

	protected byte[] getResourceBytes() {
			 String path = new StringBuilder("/")
						.append(getJarEntryPath())
						.toString();
			 InputStream in = this.getClass().getResourceAsStream(path);
			 return getByte(in);
	}
	
	/**
	 * get resource from maven repository
	 * @return
	 * @throws UnsupportedEncodingException 
	 */
	private String getJarPath() throws UnsupportedEncodingException {
			 String jarPath = this.getClass().getProtectionDomain().
			 			getCodeSource().getLocation().getPath();
			 return URLDecoder.decode(jarPath, "UTF-8");
	}

	protected boolean write(byte[] clzss) {
			if (jarPath.endsWith(".jar")) {
					return replaceJar(clzss);
			} else {
					return replaceClass(clzss);
			}
	}

	/**
	 * 替换class文件
	 * @param clzss
	 */
	protected boolean replaceClass(byte[] clzss) {
			FileOutputStream output = null;
			try {
					output = new FileOutputStream(new File(jarPath,entryName));
					output.write(clzss);
			} catch (Exception e) {
					e.printStackTrace();
					return false;
			} finally {
					close(null,output);
			}
			return true;
	}

	/**
	 * 替换jar包
	 * @param clzss
	 */
	private boolean replaceJar(byte[] clzss) {
			JarOutputStream out = null;
			try {
					Iterator<Entry<JarEntry, byte[]>> iterator = read().entrySet().iterator();
					out = new JarOutputStream(new FileOutputStream(new File(jarPath)));
					boolean has = false;
					while (iterator.hasNext()) {
							Entry<JarEntry, byte[]> next = iterator.next();
							JarEntry oldEntry = next.getKey();
							// TODO jar file
							JarEntry jeNew = null;
							byte[] b;
							// TODO create new jar entry
							if (equals(oldEntry.getName(),entryName)) {
									jeNew = new JarEntry(entryName);
									b = clzss;
									has = true;
							} else {
									jeNew = new JarEntry(oldEntry);
									b = (byte[]) next.getValue();
							}
							out.putNextEntry(jeNew);
							out.write(b, 0, b.length);
					}
					if (!has) {
							JarEntry jeNew = new JarEntry(entryName);
							out.putNextEntry(jeNew);
							out.write(clzss, 0, clzss.length);
					}
			} catch (Exception e) {
					e.printStackTrace();
					return false;
			} finally {
					close(null, out);
			}
			return true;		
	}
	
	private byte[] getByte(InputStream in) {
			try {
					byte[] bs = new byte[in.available()];
					in.read(bs);
					return bs;
			} catch (Exception e) {
			} finally {
					close(in, null);
			}
			return null;
	}

	/**
	 * 关闭流
	 * @param output
	 */
	protected void close(InputStream input,OutputStream output) {
			if (input != null) {
					try {
							input.close();
					} catch (IOException e) {
							e.printStackTrace();
					}
			}
			if (output != null) {
					try {
							output.flush();
							output.close();
					} catch (IOException e) {
							e.printStackTrace();
					}
			}
	}
	
	/**
	 * 提供不精确字符串等值比较
	 * @param o1
	 * @param o2
	 * @return
	 */
	protected boolean equals(String o1,String o2) {
			return o1.hashCode() == o2.hashCode();
	}
	
	/**
	 * rebuild maven project
	 * @throws IOException 
	 */
	protected synchronized void rebuild() {
			info("[INFO] check excel handler is update, The project will be rebuild...");
			// TODO rebuld reday
			String command = command(System.getProperties());
			System.out.println(command);
			Runtime runtime = Runtime.getRuntime();
			Process exec = null;
			try {
					exec = runtime.exec(command);
					printProcess(exec.getInputStream());
					printProcess(exec.getErrorStream());
			} catch (Exception e) {
						info("[ERROR] rebuild maven project error");
			} finally {
					close(null, exec.getOutputStream());
					System.exit(0);
			}
			
	}
	
	private void printProcess(InputStream input) throws IOException {
			BufferedReader br = new BufferedReader(new InputStreamReader(input, "UTF8"));
			String line;
			while ((line = br.readLine()) != null) {
					info(line);
			}
			close(input, null);
	}

	private String command(Properties properties) {
			StringBuilder builder = new StringBuilder("java");
			builder.append(" -Dmaven.rebuild=true");
			builder.append(" -Dmaven.home=").append(properties.get("maven.home"));
			builder.append(" -Dmaven.multiModuleProjectDirectory=").append(properties.get("maven.multiModuleProjectDirectory"));
			builder.append(" -Dclassworlds.conf=").append(properties.get("classworlds.conf"));
			builder.append(" -Dfile.encoding=").append(properties.get("file.encoding"));
			builder.append(" -classpath ").append(properties.get("java.class.path"));
			builder.append(" ").append(((String)properties.get("sun.java.command")).replaceAll("clean", ""));
			builder.append(" -f ").append(properties.get("user.dir")).append(File.separator).append("pom.xml");
			return builder.toString();
	}
	
	public static PrintStream logger = System.out;
	
	public static void info(String message) {
			logger.println(message);
	}
	
}
